package com.allen.imsystem.friend.service.impl;

import com.allen.imsystem.chat.model.pojo.PrivateChat;
import com.allen.imsystem.chat.service.PrivateChatService;
import com.allen.imsystem.common.cache.CacheExecutor;
import com.allen.imsystem.common.cache.impl.FriendCache;
import com.allen.imsystem.common.exception.BusinessException;
import com.allen.imsystem.common.exception.ExceptionType;
import com.allen.imsystem.friend.mappers.FriendApplyMapper;
import com.allen.imsystem.friend.mappers.FriendGroupMapper;
import com.allen.imsystem.friend.mappers.FriendMapper;
import com.allen.imsystem.friend.model.param.ApplyAddFriendParam;
import com.allen.imsystem.friend.model.pojo.Friend;
import com.allen.imsystem.friend.model.pojo.FriendApply;
import com.allen.imsystem.friend.model.pojo.FriendGroup;
import com.allen.imsystem.friend.model.vo.FriendApplicationView;
import com.allen.imsystem.friend.service.FriendGroupService;
import com.allen.imsystem.friend.service.FriendQueryService;
import com.allen.imsystem.friend.service.FriendUpdateService;
import com.allen.imsystem.message.service.NotifyService;
import com.allen.imsystem.user.model.dto.UidABHelper;
import com.allen.imsystem.user.model.vo.UserInfoView;
import com.allen.imsystem.user.service.UserInfoService;
import com.allen.imsystem.user.service.UserService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @ClassName FriendApplyService
 * @Description 好友更新相关业务逻辑实现
 * @Version 1.0
 */
@Service
public class FriendUpdateServiceImpl extends ServiceImpl<FriendMapper, Friend> implements FriendUpdateService {

    @Autowired
    private FriendApplyMapper friendApplyMapper;

    @Autowired
    private PrivateChatService privateChatService;

    @Autowired
    private UserService userService;

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private FriendQueryService friendQueryService;

    @Autowired
    private FriendGroupService friendGroupService;

    @Autowired
    private NotifyService notifyService;

    @Autowired
    private FriendCache friendCache;


    /**
     * 申请加好友
     *
     * @param params 申请加好友的参数类
     * @param uid    用户uid
     */
    @Override
    public boolean addFriendApply(ApplyAddFriendParam params, String uid) {
        String fromUId = uid;
        String toUId = params.getFriendId();
        String reason = params.getApplicationReason();
        if (reason == null) reason = "";

        // 删掉旧申请

        friendApplyMapper.delete(
                Wrappers.<FriendApply>lambdaUpdate()
                        .eq(FriendApply::getFromUid, fromUId)
                        .eq(FriendApply::getToUid, toUId)
        );
        Integer groupId = null;

        if (params.getGroupId() == null) {
            FriendGroup defaultGroup = friendGroupService.getUserDefaultFriendGroup(uid);
            groupId = defaultGroup.getId();
        } else {
            groupId = Integer.valueOf(params.getGroupId());
        }
        //TODO 如果我把对方删了，对方没有删我，直接默默加回好友，不需要走正常的申请流程

        FriendApply friendApply = new FriendApply(fromUId, toUId, groupId, reason);
        boolean insertSuccess = friendApplyMapper.insert(friendApply) > 0;
        if (insertSuccess) {
            // 新启动一个线程推送通知
            String finalReason = reason;
            new Thread(() -> {
                notifyService.saveAndPushNewApplyNotify(uid, params.getFriendId(), finalReason, friendApply.getId());
            }).start();
        }
        return insertSuccess;
    }

    /**
     * 通过好友申请
     *
     * @param uid      用户uid
     * @param friendId 好友uid
     * @param groupId  要放入的分组id
     */
    @Override
    @Transactional
    public void passFriendApply(String uid, String friendId, Integer groupId) {
        UidABHelper uidAB = UidABHelper.sortAndCreate(uid, friendId);
        boolean uidIsA = uid.equals(uidAB.getUidA());
        // 1 查询对方要把ta放到什么组
        FriendApply friendApply = friendApplyMapper.selectOne(
                Wrappers.<FriendApply>lambdaQuery()
                        .eq(FriendApply::getFromUid, friendId)
                        .eq(FriendApply::getToUid, uid)
                        .eq(FriendApply::getPass, false)
        );
        if (friendApply == null) {
            return;
        }
        Integer bePutInGroupId = friendApply.getGroupId();

        // 如果没有设定组，则默认放入默认组
        if (groupId == null) {
            FriendGroup defaultGroup = friendGroupService.getUserDefaultFriendGroup(uid);
            groupId = defaultGroup.getId();
        }
        // 2 更新用户申请表，将对方对当前用户的申请通过，同时也把当前用户对对方的申请全部通过
        boolean successUpdate = new LambdaUpdateChainWrapper<>(friendApplyMapper)
                .eq(FriendApply::getFromUid, friendId)
                .eq(FriendApply::getToUid, uid)
                .eq(FriendApply::getPass, false)
                .set(FriendApply::getPass, true)
                .set(FriendApply::getHasRead, true)
                .update();
        successUpdate = successUpdate | new LambdaUpdateChainWrapper<>(friendApplyMapper)
                .eq(FriendApply::getFromUid, uid)
                .eq(FriendApply::getToUid, friendId)
                .eq(FriendApply::getPass, false)
                .set(FriendApply::getPass, true)
                .set(FriendApply::getHasRead, true)
                .update();
        if (!successUpdate) { // 如果更新行数为0，说明不存在此申请或者申请已经被同意
            throw new BusinessException(ExceptionType.APPLY_HAS_BEEN_HANDLER);
        }
        // 3 处理好友关系
        Friend friend = friendQueryService.getFriendRelation(uid, friendId);
        int type = getFriendRelationType(uidIsA, friend);
        switch (type) {
            case 1: {
                if (uidIsA) {
                    Friend newFriend = new Friend(uid, friendId, bePutInGroupId, groupId);
                    this.save(newFriend);
                } else {
                    Friend newFriend = new Friend(friendId, uid, groupId, bePutInGroupId);
                    this.save(newFriend);
                }
                break;
            }
            case 2:
            case 3:
            case 4:
            case 5: {
                friend.setADeleteB(false);
                friend.setBDeleteA(false);
                if (uidIsA) {
                    friend.setBInAFgroupId(groupId);
                    friend.setAInBFgroupId(bePutInGroupId);
                } else {
                    friend.setAInBFgroupId(groupId);
                    friend.setBInAFgroupId(bePutInGroupId);
                }
                this.updateById(friend);
                break;
            }
        }

        incrGroupSizeByType(groupId, bePutInGroupId, type);


        // 4 如果他们之间还不存在会话，则新建一个新的会话
        PrivateChat privateChat = privateChatService.getByUidAB(uidAB.getUidA(), uidAB.getUidB());
        if (privateChat == null) {
            new Thread(() -> {
                privateChatService.init(uid, friendId, false);
            }).start();
        }

        // 6 更新缓存
        addFriendIntoRedis(uid, friendId);
        addFriendIntoRedis(friendId, uid);

        // 7 新好友通知
        sendNewFriendNotify(uid, friendId, friendApply, bePutInGroupId, type, groupId);
    }

    /**
     * 根据当前好友关系去递增好友分组的大小
     * @param groupId           我将要把好友放到哪个组
     * @param bePutInGroupId    我会被放到好友的哪个组
     * @param type              关系类型
     */
    private void incrGroupSizeByType(Integer groupId, Integer bePutInGroupId, int type) {
        switch (type) {
            case 1:
            case 2: {   // 双方删除或非好友，两边的组都要+1
                friendGroupService.incrFriendGroupSize(groupId, 1);
                friendGroupService.incrFriendGroupSize(bePutInGroupId, 1);
                break;
            }
            case 3: {   // 我删了对方，那么我自己的组+1
                friendGroupService.incrFriendGroupSize(groupId, 1);
                break;
            }
            case 4: {   // 对方删了我，对方的组+1
                friendGroupService.incrFriendGroupSize(bePutInGroupId, 1);
                break;
            }
            // 如果原来就是好友了，那么就不用+1
        }
    }

    private void sendNewFriendNotify(String uid, String friendId, FriendApply friendApply, Integer bePutInGroupId, int type, Integer finalGroupId) {
        new Thread(() -> {
            switch (type) {
                case 1:
                case 2: {
                    // 给好友推送
                    notifyService.saveAndPushNewFriendNotify(friendId, uid, friendApply.getId(), bePutInGroupId);
                    // 给自己推送
                    notifyService.saveAndPushNewFriendNotify(uid, friendId, friendApply.getId(), finalGroupId);
                    break;
                }
                case 3: {
                    // 给自己推送
                    notifyService.saveAndPushNewFriendNotify(uid, friendId, friendApply.getId(), finalGroupId);
                    break;
                }
                case 4: {
                    // 给好友推送
                    notifyService.saveAndPushNewFriendNotify(friendId, uid, friendApply.getId(), bePutInGroupId);
                    break;
                }
            }
        }).start();
    }

    /**
     * 分析获取好友关系的类型
     */
    private int getFriendRelationType(boolean uidIsA, Friend friend) {
        int type;
        if (friend == null) {
            type = 1;
        } else {
            boolean aDeleteB = friend.getADeleteB();
            boolean bDeleteA = friend.getBDeleteA();
            if (aDeleteB && bDeleteA) {
                // 3.2 双方删除
                type = 2;
            } else if (uidIsA && aDeleteB || !uidIsA && bDeleteA) {
                // 3.3 我把对方删了，对方没有删我
                type = 3;
            } else if (uidIsA && bDeleteA || !uidIsA && aDeleteB) {
                // 3.4 对方删了我，我没有删对方
                type = 4;
            } else {
                // 3.5 双向都是好友
                type = 5;
            }
        }
        return type;
    }


    /**
     * 获取好友申请列表
     *
     * @param uid 用户uid
     * @return 好友分组列表
     */
    @Override
    public List<FriendApplicationView> getFriendApplicationList(String uid) {
        List<FriendApplicationView> friendApplicationList = friendApplyMapper.selectLatestApply(uid, 50);
        for (FriendApplicationView friendApplication : friendApplicationList) {
            UserInfoView userInfo = userInfoService.findUserInfoView(friendApplication.getFromUid());
            friendApplication.setApplicantInfo(userInfo);
        }
        return friendApplicationList;
    }

    /**
     * 删除好友
     *
     * @param uid      用户uid
     * @param friendId 要删除的好友uid
     * @return 是否删除成功
     */
    @Override
    @Transactional
    public Long deleteFriend(String uid, String friendId) {
        Friend friend = friendQueryService.getFriendRelation(uid, friendId);
        Integer myFriendGroup = null;
        // 1、检查是否已经被好友删除
        boolean isDeletedByFriend;
        if (uid.equals(friend.getUidA())) {
            isDeletedByFriend = friend.getBDeleteA();
            myFriendGroup = friend.getBInAFgroupId();
        } else {
            isDeletedByFriend = friend.getADeleteB();
            myFriendGroup = friend.getAInBFgroupId();
        }
        if (isDeletedByFriend)//如果已经被对方删除，则执行物理删除
        {
            //物理删除好友关系
            this.removeById(friend);
        } else {   // 否则执行逻辑删除
            sortDeleteFriend(uid, friendId);
        }
        // 好友分组的好友人数-1
        friendGroupService.incrFriendGroupSize(myFriendGroup, -1);
        // 移除掉与好友的会话
        Long chatId = privateChatService.remove(uid, friendId);
        // 2、更新缓存
        removeFriendFromRedis(uid, friendId);
        removeFriendFromRedis(friendId, uid);
        return chatId;
    }

    private void sortDeleteFriend(String uid, String friendId) {
        if (uid.compareTo(friendId) < 0) {
            this.lambdaUpdate()
                    .eq(Friend::getUidA, uid)
                    .eq(Friend::getUidB, friendId)
                    .set(Friend::getADeleteB, true)
                    .update();
        } else {
            this.lambdaUpdate()
                    .eq(Friend::getUidB, uid)
                    .eq(Friend::getUidA, friendId)
                    .set(Friend::getBDeleteA, true)
                    .update();
        }
    }

    /**
     * 添加一个好友到redis中某用户的好友列表（如果存在缓存）
     */
    private void addFriendIntoRedis(String uid, String friendId) {
        if (CacheExecutor.exist(friendCache.friendSet, uid)) {
            CacheExecutor.add(friendCache.friendSet, uid, friendId);
        }
    }

    /**
     * 从redis缓存中移除一个好友（如果存在缓存）
     */
    private void removeFriendFromRedis(String uid, String friendId) {
        if (CacheExecutor.exist(friendCache.friendSet, uid)) {
            CacheExecutor.remove(friendCache.friendSet, uid, friendId);
        }
    }
}
